import io
import logging

from django.contrib.auth.models import User
from django.http import HttpResponse
from django.shortcuts import render
from django.views import View
from django_redis import get_redis_connection
from fdfs_client.client import get_tracker_conf, Fdfs_client
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet
from rest_framework_extensions.cache.mixins import CacheResponseMixin
from rest_framework_simplejwt.views import TokenObtainPairView

from config.dbs.RedisConfig import EXPIRE_TIME, KEY_TEMPLATE
from users.models import Area, Address
from users.serializers import UserSerializer, MyTokenObtainPairSerializer, UserDetailSerializer, \
    UpdatePasswordSerializer, AreaSerializer, ParentAreaSerializer, AddressSerializer
from utils.VerifyUtil import ImageVerifyUtil


# Create your views here.

class ImageVerifyView(View):
    def get(self, request, func, uuid):
        """
        获取验证码数据  并且返回给前端页面
        :param request:
        :param uuid:
        :return:
        """
        imageVerify = ImageVerifyUtil()
        img, code = imageVerify.verify_code()

        # 创建二进制字节流对象  用于暂存验证码二进制数据
        img_bytes = io.BytesIO()
        # 将验证码图片以PNG格式保存在img_bytes字节流对象中
        img.save(img_bytes, format='PNG')
        # 获取img_bytes字节流缓冲区中所有的字节数据(二维码图片而二进制数据)
        image_bytes = img_bytes.getvalue()
        # content_type='image/png' 告诉浏览器这是一张PNG格式的图片使浏览器能够正确渲染

        # 与redis创建链接 并且以 (键,值,过期时间)形式对数据进行保存
        cache = get_redis_connection(alias='verify_codes')
        cache.set(KEY_TEMPLATE % (func, uuid), code, EXPIRE_TIME)

        return HttpResponse(image_bytes, content_type='image/png')

logger = logging.getLogger(__name__)
tracker_path=get_tracker_conf('utils/fastdfs/client.conf')
client=Fdfs_client(tracker_path)

class UserViewSet(viewsets.GenericViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def get_serializer_class(self):
        if self.action in ['info']:
            return UserDetailSerializer
        if self.action in ['password']:
            return UpdatePasswordSerializer
        else:
            return self.serializer_class

    def get_permissions(self):
        """
        为方法函数添加权限
        :return:
        """
        if self.action in ['now','info']:
            permission_classes = [IsAuthenticated()]    #IsAuthenticated()登录权限类
        else:
            permission_classes = []
        return permission_classes

    def create(self, request, *args, **kwargs):
        """
        用户注册
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        # 得到数据进行序列化
        serializer = self.get_serializer(data=request.data)
        # 进行数据校验
        serializer.is_valid(raise_exception=True)
        # 数据校验成功就对该用户数据进行持久化保存到数据库中
        # 从前端获取用户注册数据
        username = request.data.get('username')
        password = request.data.get('password')

        # 数据保存到数据库中
        user = User(username=username)
        user.set_password(password)
        user.save()
        return Response(self.get_serializer(user).data)

    @action(methods=['get'],detail=False)
    def now(self,request):
        """
        登录后进行数据获取
        :param request:
        :return:
        """
        serializer = self.get_serializer(request.user)
        return Response(serializer.data)

    @action(methods=['put'],detail=False)
    def info(self, request):
        """
        用户个人信息页面和修改个人信息功能
        :param request:
        :return:
        """
        # 得到用户数据
        user = request.user
        # 无论传入是谁的id都覆盖为当前用户的id
        request.POST._mutable = True
        request.data['user']=user.id

    # 用户头像修改
        #获取用户传入的头像数据
        avatar_file = request.FILES.get('file')
        if avatar_file:
            # 如果有文件 经过判断后将文件上传
            # 判断文件类型是否为规定文件
            if avatar_file.content_type not in ('image/png', 'image/jpg', 'image/jpeg'):
                return Response(status=status.HTTP_400_BAD_REQUEST)
            # 如果为规定文件再判断拓展名称是否正常，不正常就将报错保存到日志并且进行修改
            try:
                file_ext_name = avatar_file.name.split('.')[-1]
            except Exception as e:
                logger.info('文件拓展名异常：%s'%e)
                file_ext_name = 'png'

            #图片数据合规后将图片数据交给fastdfs，上传到storage服务上
            # django不能得到图片文件只能得到文件数据 这里通过upload_by_buffer获取read()读取到的图片数据
            # 上传时还要将文件后缀名传入
            try:
                client_upload = client.upload_by_buffer(avatar_file.read(), file_ext_name=file_ext_name)
            except Exception as e:
                logger.error('图片上传异常：%s',e)
                return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            else:
            # 判断上传是否成功Status=='Upload successed.'
                if client_upload.get('Status') != 'Upload successed.':
                    logger.warning('图片上传失败')
                    return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            # 上传成功就将上传到storage中的图片数据进行解码decode保存到request数据
            image_name = client_upload.get('Remote file_id').decode()
            request.data['avatar']=image_name

    # 个人信息修改
        # 判断是都有用户的详情信息
        # 没有就创建有就修改
        # 再进行校验保存操作
    # 这里用到的序列化器是UserDetailSerializer但是self.get_serializer返回的是视图集默认的序列化器UserSerializer
    # 所以我们要重写get_serializer_class对序列化器进行选择
        if hasattr(user,'userdetail'):
            if user.userdetail.avatar:
            # 修改数据 重点在图片数据的修改 因为图片数据要保存到storage中 要把原来的图片从storage删除  上面已经将新的图片保存了
            # 先将原图片数据删除
                client.delete_file(user.userdetail.avatar.encode())
            user_detail_serializer = self.get_serializer(user.userdetail,data=request.data)  # 对谁进行序列化操作 数据是上面
        else:
            user_detail_serializer = self.get_serializer(data=request.data)

        user_detail_serializer.is_valid(raise_exception=True)
        user_detail_serializer.save()
        return Response(user_detail_serializer.data)
        # 最后还需对info方法进行登录权限的添加

    @action(methods=['put'],detail=False)
    def password(self, request):
        """
        修改密码功能
        :param request:
        :return:
        """
        user = request.user
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        new_password = request.data.get('new_password')
        user.set_password(new_password)
        user.save()
        return Response({'msg':'修改成功'})

class AreaViewSet(CacheResponseMixin,ReadOnlyModelViewSet): # 继承CacheResponseMixin缓存类
    queryset = Area.objects.all()
    serializer_class = AreaSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        if self.action in ['list']:
            return self.queryset.filter(parent__isnull=True)
        return self.queryset

    def get_serializer_class(self):
        if self.action in ['retrieve']:
            return ParentAreaSerializer
        return self.serializer_class

class AddressViewSet(ModelViewSet):
    queryset = Address.objects.all()
    serializer_class = AddressSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
    # 因为每个账号都只能对自己账号的收货地址进行增删改查
    # 利用管理器 九百昵称只能对收货地址进行增删改查
    # self.request.user 当前登录用户对象
        return self.request.user.address_set.all()

    def create(self, request, *args, **kwargs):
        max_count = 5
        if self.get_queryset().count() >= max_count:
            return Response({
                'detail':f'收货地址数量超过{max_count}条上限'
            },status=status.HTTP_400_BAD_REQUEST)

        request.POST._mutable = True
        request.data['user']=request.user.id
        return ModelViewSet.create(self, request, *args, **kwargs)

class MyTokenObtainPairViewSet(TokenObtainPairView):
    serializer_class = MyTokenObtainPairSerializer